﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Threading;

namespace iPdf
{
    public enum ItemStatus
    {
        Idle = 0, 
        Processing = 1,
        Done = 2,
        Error = 3
    }

    public struct ActionReport
    {
        public int DoneItemsCount;
        public int UndoneItemsCount;
        public int ErrorItemsCount;
    }

    public class ProcessItemEventArgs: EventArgs
    {
        public int Index;
        public ItemStatus Status;
        public ActionReport Report;
        public TataException Exception;

        public ProcessItemEventArgs(int index)
        {
            Index = index;
        }

        public ProcessItemEventArgs(int index, ItemStatus status, TataException exception)
        {
            Index = index;
            Status = status;
            Exception = exception;
        }

        public ProcessItemEventArgs(ActionReport report)
        {
            Report = report;
        }
    }
    
    public abstract class Actor
    {
        #region Fields
        public event EventHandler<ProcessItemEventArgs> ItemStarted;
        public event EventHandler<ProcessItemEventArgs> ItemFinished;
        public event EventHandler<ProcessItemEventArgs> AllItemsFinished;

        protected BackgroundWorker worker;
        protected PageOption option;
        #endregion

        #region Properties

        protected PageOption ActOption
        {
            get
            {
                return option;
            }
        }
            

        #endregion

        #region Ctor
        protected Actor()
        {
            worker = new BackgroundWorker();
            worker.DoWork += new DoWorkEventHandler(DoWork);
        }
        #endregion

        #region Methods
        private void DoWork(Object sender, DoWorkEventArgs e)
        {
            Thread.CurrentThread.Priority = ThreadPriority.Lowest;
            ActionItem[] items = (ActionItem[])e.Argument;
            try
            {
                Prepare();
            }
            catch (ActionCancelled)
            {
                NotPrepared(items);
                return;
            }
            catch (TataException tataEx)
            {
                Global.MainWindow.ShowError(tataEx);
                NotPrepared(items);
                return;
            }
            catch (Exception ex)
            {
                Global.MainWindow.ShowError(new TataException(ex.Message));
                NotPrepared(items);
                return;
            }

            ActionReport report = new ActionReport();

            report.UndoneItemsCount = items.Length;
            report.DoneItemsCount = report.ErrorItemsCount = 0;

            ItemStatus curItemStatus;
            int curItemIndex = 1;
            foreach (ActionItem item in items)
            {
                if (MainForm.ActionCancelled)
                {
                    break;
                }

                if (ItemStarted != null)
                {
                    ItemStarted(this, new ProcessItemEventArgs(curItemIndex));
                }

                TataException exception = null;
                try
                {
                    WorkOnItem(item);
                    report.DoneItemsCount++;
                    curItemStatus = ItemStatus.Done;
                }
                catch (TataException ex)
                {
                    report.ErrorItemsCount++;
                    curItemStatus = ItemStatus.Error;
                    exception = ex;
                }
                catch (Exception ex)
                {
                    report.ErrorItemsCount++;
                    curItemStatus = ItemStatus.Error;
                    exception = new TataException(ex.Message);
                }
                
                --report.UndoneItemsCount;
                if (ItemFinished != null)
                {
                    ItemFinished(this, new ProcessItemEventArgs(curItemIndex, curItemStatus, exception));
                }

                ++curItemIndex;
            }

            try
            {
                Finish();
            }
            catch(Exception ex)
            {
                Global.MainWindow.ShowError(new TataException(ex.Message));
            }
            finally
            {
                if (AllItemsFinished != null)
                {
                    AllItemsFinished(this, new ProcessItemEventArgs(report));
                }
            }
        }

        protected virtual void WorkOnItem(ActionItem item)
        {
            Helper.VerifyFile(item.Source);
        }

        protected void NotPrepared(ActionItem[] items)
        {
            if (AllItemsFinished != null)
            {
                ActionReport report = new ActionReport();
                report.ErrorItemsCount = 0;
                report.DoneItemsCount = 0;
                report.UndoneItemsCount = items.Length;
                AllItemsFinished(this, new ProcessItemEventArgs(report));
            }
        }

        public void Execute(ActionItem[] items, PageOption opt)
        {
            option = opt;

            worker.RunWorkerAsync(items);
        }

        protected virtual void Prepare()
        {
        }

        protected virtual void Finish()
        {
            Helper.CollectGarbage();
        }

        #endregion
    }
}
